package com.mhb.generator.config;

import com.mhb.generator.config.builder.ConfigBuilder;
import com.mhb.generator.config.convert.MySqlTypeConvert;
import com.mhb.generator.config.interfaces.DbQueryInterface;
import com.mhb.generator.config.interfaces.TypeConvertInterface;
import com.mhb.generator.config.po.TableField;
import com.mhb.generator.config.po.TableInfo;
import com.mhb.generator.config.rules.FileType;
import com.mhb.generator.config.rules.NamingStrategy;
import com.mhb.generator.util.ArrayUtils;
import com.mhb.generator.util.ExceptionUtils;
import com.mhb.generator.util.JdbcUtil;
import com.mhb.generator.util.StringUtil;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;

import static java.util.stream.Collectors.joining;

/**
 * @author 博博
 * @Title: AutoGenerator
 * @time 2019/12/27 0027 11:04
 */
@Data
@Accessors(chain = true)
public class AutoGenerator {

    /**
     * 数据源配置
     */
    private DataSourceConfig dataSourceConfig;

    /**
     * 全局配置
     */
    private GlobalConfig globalConfig;

    /**
     * 包名配置
     */
    private PackageConfig packageConfig;

    /**
     * 策略配置
     */
    private StrategyConfig strategyConfig;

    /**
     * 视图配置
     */
    private ViewConfig viewConfig;

    /**
     * 执行
     */
    public void execution() {

        if (this.getGlobalConfig() == null) this.setGlobalConfig(new GlobalConfig());
        if (this.getPackageConfig() == null) throw ExceptionUtils.mpe("请填写PackageConfig配置.");
        if (this.getStrategyConfig() == null) this.setStrategyConfig(new StrategyConfig());
        if (this.getViewConfig() == null) this.setViewConfig(new ViewConfig());

        // 获取db query
        DbQueryInterface dbQuery = dataSourceConfig.getDbQuery();

        try {
            // 表sql
            String tableSql = "";
            // 如果查询的表名数组为空则查询全部
            if (ArrayUtils.isEmpty(strategyConfig.getTables())) {
                tableSql = dbQuery.tablesSql();
            } else {// 否则查询指定表名
                tableSql = dbQuery.tablesSql(strategyConfig.getTables());
            }
            // 执行查询
            JdbcUtil.JdbcConn jbConn = JdbcUtil.executeQuery(dataSourceConfig.getConn(), tableSql);
            ResultSet resultSet = jbConn.getRs();
            // 表list集合
            List<TableInfo> tableInfoList = new ArrayList<>();
            while (resultSet.next()) {
                String comment = resultSet.getString(dbQuery.tableComment());
                // 不为空并且是视图则跳出进行下一次循环
                if (StringUtil.isNotNull(comment) && comment.equalsIgnoreCase("VIEW")) {
                    continue;
                }
                // 表名
                String tableName = resultSet.getString(dbQuery.tableName());
                TableInfo tableInfo = new TableInfo();
                tableInfo.setComment(comment)
                        .setName(tableName);

                // 判断是否有前缀
                if (StringUtil.isNotNull(strategyConfig.getTablePrefix())) {
                    tableName = NamingStrategy.removePrefix(tableName, strategyConfig.getTablePrefix());
                }
                // 判断是否是下划线 转换驼峰配置
                if (strategyConfig.getNamingStrategy().equals(NamingStrategy.underline_to_camel)) {
                    tableName = NamingStrategy.capitalFirst(NamingStrategy.underlineToCamel(tableName));
                }

                // 视图名称配置
                String viewName = tableName;
                if (viewConfig.isTemplateCamel()) {
                    viewName = StringUtil.camelToUnderline(tableName);
                }

                // 塞入各个文件名称
                tableInfo.setServiceName(StringUtil.format(strategyConfig.getServiceName(), tableName))
                        .setServiceImplName(StringUtil.format(strategyConfig.getServiceImplName(), tableName))
                        .setControllerName(StringUtil.format(strategyConfig.getControllerName(), tableName))
                        .setMapperName(StringUtil.format(strategyConfig.getMapperName(), tableName))
                        .setXmlName(StringUtil.format(strategyConfig.getMapperName(), tableName))
                        .setEntityName(tableName)
                        .setSelectViewName(StringUtil.format(viewConfig.getSelectViewName(), viewName))
                        .setSaveViewName(StringUtil.format(viewConfig.getSaveViewName(), viewName))
                        .setUpdateViewName(StringUtil.format(viewConfig.getUpdateViewName(), viewName))
                        .setDetailViewName(StringUtil.format(viewConfig.getDetailViewName(),viewName));
                tableInfoList.add(tableInfo);
            }
            JdbcUtil.close(jbConn);
            TypeConvertInterface convert = new MySqlTypeConvert();
            // 循环表
            for (TableInfo tableInfo : tableInfoList) {
                // 查询字段
                JdbcUtil.JdbcConn fieldConn = JdbcUtil.executeQuery(dataSourceConfig.getConn(), String.format(dbQuery.tableFieldsSql(), tableInfo.getName()));
                ResultSet rs = fieldConn.getRs();
                TableField tableField = null;
                List<TableField> tableFieldList = new ArrayList<>();
                while (rs.next()) {
                    tableField = new TableField();
                    String column = rs.getString(dbQuery.fieldName());
                    String propertyName = NamingStrategy.underline_to_camel.equals(strategyConfig.getNamingStrategy())
                            ? NamingStrategy.underlineToCamel(column) : column;
                    boolean extra = tableField.getPriAndExtra(rs, dbQuery, tableInfo);
                    // 是主键
                    if (extra) {
                        tableInfo.setPrimaryKey(column)
                                .setPrimaryKeyName(propertyName);
                    }
                    // 是否null
                    String isNull = rs.getString(dbQuery.fieldNull());
                    if("NO".equalsIgnoreCase(isNull)){
                        tableField.setNotNull(true);
                    }else{
                        tableField.setNotNull(false);
                    }
                    // 获取字段
                    String fieldType = rs.getString(dbQuery.fieldType());
                    tableField.setColumn(column)
                            .setColumnType(convert.processTypeConvert(globalConfig, fieldType))
                            .setPropertyName(propertyName)
                            .setComment(rs.getString(dbQuery.fieldComment()))
                            .setType(fieldType).setPropertyType(tableField.getColumnType().getType());

                    tableFieldList.add(tableField);
                }
                JdbcUtil.close(fieldConn);
                tableInfo.setFields(tableFieldList);
                tableInfo.getPackageNames().put("entity", parentPackageAndChild(packageConfig.getParent(), packageConfig.getEntity()));
                tableInfo.getPackageNames().put("service", parentPackageAndChild(packageConfig.getParent(), packageConfig.getService()));
                tableInfo.getPackageNames().put("service_impl", parentPackageAndChild(packageConfig.getParent(), packageConfig.getService()) + ".impl");
                tableInfo.getPackageNames().put("mapper", parentPackageAndChild(packageConfig.getParent(), packageConfig.getMapper()));
                tableInfo.getPackageNames().put("controller", parentPackageAndChild(packageConfig.getParent(), packageConfig.getController()));
            }
            pretreatmentConfigBuilder(tableInfoList);
            new ConfigBuilder(this, tableInfoList);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 存入各个模块需要导入的包
     *
     * @param tableInfo tableInfo
     */
    private void pretreatmentConfigBuilder(List<TableInfo> tableInfo) {
        for (TableInfo info : tableInfo) {
            Set<String> modelSet = new HashSet<>();
            Set<String> serviceSet = new HashSet<>();
            Set<String> serviceImplSet = new HashSet<>();
            Set<String> mapperSet = new HashSet<>();
            Set<String> controllerSet = new HashSet<>();
            for (TableField tableField : info.getFields()) {
                // 实体全限定路径
                String entityCanonicalName = parentPackageAndChild(parentPackageAndChild(packageConfig.getParent(), packageConfig.getEntity()), info.getEntityName());
                String serviceCanonicalName = parentPackageAndChild(parentPackageAndChild(packageConfig.getParent(), packageConfig.getService()), info.getServiceName());
                String mapperCanonicalName = parentPackageAndChild(parentPackageAndChild(packageConfig.getParent(), packageConfig.getMapper()), info.getMapperName());
                // 如果存储的非基础类型 则需要导入
                if (tableField.getColumnType() != null && StringUtil.isNotNull(tableField.getColumnType().getPkg())) {
                    modelSet.add(tableField.getColumnType().getPkg());
                }
                // 如果是mybatis plus 导入实体依赖包
                if (this.getGlobalConfig().isMybatisPlus()) {
//                    modelSet.add("com.baomidou.mybatisplus.extension.activerecord.Model");
                    modelSet.add("com.baomidou.mybatisplus.annotation.TableId");
                    modelSet.add("com.baomidou.mybatisplus.annotation.TableName");
                    modelSet.add("com.baomidou.mybatisplus.annotation.TableField");
                    long count = info.getFields().stream().map(TableField::isKeyIdentityFlag).filter(key -> key).count();
                    // 有用自增主键
                    if (count > 0) {
                        modelSet.add("com.baomidou.mybatisplus.annotation.IdType");
                    }
                    serviceSet.add("com.baomidou.mybatisplus.extension.service.IService");
                    serviceSet.add(entityCanonicalName);
                    serviceSet.add(mapperCanonicalName);


                    serviceImplSet.add("com.baomidou.mybatisplus.extension.service.impl.ServiceImpl");
                    serviceImplSet.add(entityCanonicalName);
                    serviceImplSet.add(mapperCanonicalName);

                    mapperSet.add("com.baomidou.mybatisplus.core.mapper.BaseMapper");
                    mapperSet.add(entityCanonicalName);
                }
                // 如果是lombok实体
                if (this.getStrategyConfig().isEntityLomBokModel()) {
                    modelSet.add(lombok.NoArgsConstructor.class.getCanonicalName());
                    modelSet.add(lombok.AllArgsConstructor.class.getCanonicalName());
                    modelSet.add(lombok.Data.class.getCanonicalName());
                    if (this.getStrategyConfig().isEntityBuilderModel()) {
                        modelSet.add(lombok.experimental.Accessors.class.getCanonicalName());
                    }
                }
                // SerialVersion
                if (this.getStrategyConfig().isEntitySerialVersionUID()) {
                    modelSet.add(Serializable.class.getCanonicalName());
                }
                if(this.getGlobalConfig().isPageMethod()){
                    serviceSet.add("com.github.pagehelper.PageInfo");
                    serviceImplSet.add("com.github.pagehelper.PageInfo");
                    serviceImplSet.add("com.github.pagehelper.PageHelper");
                    controllerSet.add("com.github.pagehelper.PageInfo");
                }
                // 基础的 curd
                if (this.getGlobalConfig().isGenerateBaseCRUD()) {
                    mapperSet.add(List.class.getCanonicalName());
                    mapperSet.add("java.io.Serializable");
                    mapperSet.add("org.apache.ibatis.annotations.Param");
                    serviceSet.add(List.class.getCanonicalName());
                    serviceSet.add(Serializable.class.getCanonicalName());
                    serviceImplSet.add(List.class.getCanonicalName());
                    serviceImplSet.add(Serializable.class.getCanonicalName());
                    if(!getGlobalConfig().isMybatisPlus() && getGlobalConfig().isGenerateBaseCRUD()){
                        serviceImplSet.add(javax.annotation.Resource.class.getCanonicalName());
                    }
                    // 不是mybatis plus版本的
                    if (!this.getGlobalConfig().isMybatisPlus()) {
                        mapperSet.add(entityCanonicalName);
                        serviceSet.add(entityCanonicalName);
                        serviceImplSet.add(serviceCanonicalName);
                        serviceImplSet.add(entityCanonicalName);
                        serviceImplSet.add(mapperCanonicalName);
                    }
                }
                serviceImplSet.add("org.springframework.stereotype.Service");
                serviceImplSet.add(serviceCanonicalName);
                controllerSet.add("org.springframework.stereotype.Controller");
                controllerSet.add(javax.annotation.Resource.class.getCanonicalName());
                controllerSet.add("org.springframework.web.bind.annotation.RequestMapping");
                // controller curd
                if (this.getGlobalConfig().isControllerCRUD()) {
                    controllerSet.add(serviceCanonicalName);
                    controllerSet.add(entityCanonicalName);
                    controllerSet.add(List.class.getCanonicalName());
                    controllerSet.add("org.springframework.ui.Model");
                    controllerSet.add("org.springframework.web.bind.annotation.GetMapping");
                    controllerSet.add("lombok.Data");
                    controllerSet.add("org.springframework.web.bind.annotation.PostMapping");
                    controllerSet.add("org.springframework.web.bind.annotation.RequestMapping");
                    controllerSet.add("org.springframework.web.bind.annotation.ResponseBody");
                    controllerSet.add("org.springframework.web.bind.annotation.PathVariable");
                    controllerSet.add("java.io.Serializable");
                    if(this.getGlobalConfig().isGenerateBaseCRUD() && this.getGlobalConfig().isGenerateTemplate()){
                        controllerSet.add("org.springframework.ui.Model");
                    }
                }
            }
            info.getImportPackages().put("entity", modelSet);
            info.getImportPackages().put("serviceImpl", serviceImplSet);
            info.getImportPackages().put("service", serviceSet);
            info.getImportPackages().put("mapper", mapperSet);
            info.getImportPackages().put("controller", controllerSet);
        }

    }


    /**
     * 获取父包名和子包名合并的结果 并分割成路径
     *
     * @param packageName 子包名
     * @return 处理后的结果
     */
    public String parentPackageAndChildPath(String packageName) {
        String parentName = this.getPackageConfig().getParent();
        packageName = parentPackageAndChild(parentName, packageName);
        // 分割为路径
        if (StringUtil.isNotNull(packageName)) {
            packageName = Arrays.asList(packageName.split("\\.")).stream().collect(joining("\\"));
        }
        return packageName;
    }

    /**
     * 获取父包名和子包名合并的结果
     *
     * @param packageName 子包名
     * @return 处理后的结果
     */
    public String parentPackageAndChild(String parentName, String packageName) {
        // 父子合并
        if (StringUtil.isNotNull(parentName)) {
            packageName = StringUtil.format("%s.%s", parentName, packageName);
        }
        return packageName;
    }

}
